]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
NotificationDaemon: support sound in notifications
authorGiovanni Campagna <gcampagna@src.gnome.org>
Mon, 5 Nov 2012 17:10:24 +0000 (18:10 +0100)
committerGiovanni Campagna <gcampagna@src.gnome.org>
Thu, 14 Feb 2013 17:54:08 +0000 (18:54 +0100)
The notifications spec has two hints for playing a sound, sound-file
and sound-name. We can support them using the existing code that
wraps libcanberra.

https://bugzilla.gnome.org/show_bug.cgi?id=642831

js/ui/messageTray.js
js/ui/notificationDaemon.js
src/shell-global.c
src/shell-global.h

index d4bd7b51e8bec4dd4ad43c0b52e094d8ac941dac..7376456b54a0246c8d1bb6ea0890a2760b8fe2fa 100644 (file)
@@ -331,6 +331,10 @@ Signals.addSignalMethods(NotificationPolicy.prototype);
 // the content and the action area of the notification will be cleared.
 // The content area is also always cleared if 'customContent' is false
 // because it might contain the @banner that didn't fit in the banner mode.
+//
+// If @params contains 'soundName' or 'soundFile', the corresponding
+// event sound is played when the notification is shown (if the policy for
+// @source allows playing sounds).
 const Notification = new Lang.Class({
     Name: 'Notification',
 
@@ -360,6 +364,9 @@ const Notification = new Lang.Class({
         this._spacing = 0;
         this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
         this._imageBin = null;
+        this._soundName = null;
+        this._soundFile = null;
+        this._soundPlayed = false;
 
         source.connect('destroy', Lang.bind(this,
             function (source, reason) {
@@ -426,7 +433,9 @@ const Notification = new Lang.Class({
                                         titleMarkup: false,
                                         bannerMarkup: false,
                                         bodyMarkup: false,
-                                        clear: false });
+                                        clear: false,
+                                        soundName: null,
+                                        soundFile: null });
 
         this._customContent = params.customContent;
 
@@ -524,6 +533,14 @@ const Notification = new Lang.Class({
 
         if (params.body)
             this.addBody(params.body, params.bodyMarkup);
+
+        if (this._soundName != params.soundName ||
+            this._soundFile != params.soundFile) {
+            this._soundName = params.soundName;
+            this._soundFile = params.soundFile;
+            this._soundPlayed = false;
+        }
+
         this.updated();
     },
 
@@ -884,6 +901,38 @@ const Notification = new Lang.Class({
                (!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
     },
 
+    playSound: function() {
+        if (this._soundPlayed)
+            return;
+
+        if (!this.source.policy.enableSound) {
+            this._soundPlayed = true;
+            return;
+        }
+
+        if (this._soundName) {
+            if (this.source.app) {
+                let app = this.source.app;
+
+                global.play_theme_sound_full(0, this._soundName,
+                                             this.title, null,
+                                             app.get_id(), app.get_name());
+            } else {
+                global.play_theme_sound(0, this._soundName, this.title, null);
+            }
+        } else if (this._soundFile) {
+            if (this.source.app) {
+                let app = this.source.app;
+
+                global.play_sound_file_full(0, this._soundFile,
+                                            this.title, null,
+                                            app.get_id(), app.get_name());
+            } else {
+                global.play_sound_file(0, this._soundFile, this.title, null);
+            }
+        }
+    },
+
     updated: function() {
         if (this.expanded)
             this.expand(false);
@@ -1233,8 +1282,16 @@ const Source = new Lang.Class({
         notification.acknowledged = false;
         this.pushNotification(notification);
 
-        if (!this.isMuted && this.policy.showBanners)
-            this.emit('notify', notification);
+        if (!this.isMuted) {
+            // Play the sound now, if banners are disabled.
+            // Otherwise, it will be played when the notification
+            // is next shown.
+            if (this.policy.showBanners) {
+                this.emit('notify', notification);
+            } else {
+                notification.playSound();
+            }
+        }
     },
 
     destroy: function(reason) {
@@ -1998,9 +2055,10 @@ const MessageTray = new Lang.Class({
             } else {
                 // The summary box pointer is showing or shown (otherwise,
                 // this._summaryBoxPointerItem would be null)
-                // Immediately mark the notification as acknowledged, as it's
-                // not going into the queue
+                // Immediately mark the notification as acknowledged and play its
+                // sound, as it's not going into the queue
                 notification.acknowledged = true;
+                notification.playSound();
             }
 
             return;
@@ -2434,6 +2492,7 @@ const MessageTray = new Lang.Class({
 
     _updateShowingNotification: function() {
         this._notification.acknowledged = true;
+        this._notification.playSound();
 
         // We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
         // is on in the control center.
index e02bf22c315e4e3c1b6e9acfab4c7b88c2fffd0c..28d2cfbdaea8a339dbfc2c91c047c7dbed0142f7 100644 (file)
@@ -527,7 +527,9 @@ const NotificationDaemon = new Lang.Class({
 
         notification.update(summary, body, { gicon: gicon,
                                              bannerMarkup: true,
-                                             clear: true });
+                                             clear: true,
+                                             soundFile: hints['sound-file'],
+                                             soundName: hints['sound-name'] });
         notification.setImage(image);
 
         if (actions.length) {
@@ -582,7 +584,7 @@ const NotificationDaemon = new Lang.Class({
             // 'icon-multi',
             'icon-static',
             'persistence',
-            // 'sound',
+            'sound',
         ];
     },
 
index 7344334711b62c4dd2560b39a9c87ada8ff7ee95..e8559ebca4206dcd38cd483d5034b5752392ddda 100644 (file)
@@ -1537,11 +1537,12 @@ shell_global_run_at_leisure (ShellGlobal         *global,
 
 static void
 build_ca_proplist_for_event (ca_proplist  *props,
+                             const char   *event_property,
                              const char   *event_id,
                              const char   *event_description,
                              ClutterEvent *for_event)
 {
-  ca_proplist_sets (props, CA_PROP_EVENT_ID, event_id);
+  ca_proplist_sets (props, event_property, event_id);
   ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description);
   ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
 
@@ -1589,7 +1590,7 @@ shell_global_play_theme_sound (ShellGlobal  *global,
   ca_proplist *props;
 
   ca_proplist_create (&props);
-  build_ca_proplist_for_event (props, name, description, for_event);
+  build_ca_proplist_for_event (props, CA_PROP_EVENT_ID, name, description, for_event);
 
   ca_context_play_full (global->sound_context, id, props, NULL, NULL);
 
@@ -1621,7 +1622,7 @@ shell_global_play_theme_sound_full (ShellGlobal  *global,
   ca_proplist *props;
 
   ca_proplist_create (&props);
-  build_ca_proplist_for_event (props, name, description, for_event);
+  build_ca_proplist_for_event (props, CA_PROP_EVENT_ID, name, description, for_event);
   ca_proplist_sets (props, CA_PROP_APPLICATION_ID, application_id);
   ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, application_name);
 
@@ -1630,6 +1631,68 @@ shell_global_play_theme_sound_full (ShellGlobal  *global,
   ca_proplist_destroy (props);
 }
 
+/**
+ * shell_global_play_sound_file_full:
+ * @global: the #ShellGlobal
+ * @id: an id, used to cancel later (0 if not needed)
+ * @file_name: the file name to play
+ * @description: the localized description of the event that triggered this alert
+ * @for_event: (allow-none): a #ClutterEvent in response to which the sound is played
+ * @application_id: application on behalf of which the sound is played
+ * @application_name:
+ *
+ * Like shell_global_play_theme_sound_full(), but with an explicit path
+ * instead of a themed sound.
+ */
+void
+shell_global_play_sound_file_full  (ShellGlobal  *global,
+                                    guint         id,
+                                    const char   *file_name,
+                                    const char   *description,
+                                    ClutterEvent *for_event,
+                                    const char   *application_id,
+                                    const char   *application_name)
+{
+  ca_proplist *props;
+
+  ca_proplist_create (&props);
+  build_ca_proplist_for_event (props, CA_PROP_MEDIA_FILENAME, file_name, description, for_event);
+  ca_proplist_sets (props, CA_PROP_APPLICATION_ID, application_id);
+  ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, application_name);
+
+  ca_context_play_full (global->sound_context, id, props, NULL, NULL);
+
+  ca_proplist_destroy (props);
+}
+
+/**
+ * shell_global_play_sound_file:
+ * @global: the #ShellGlobal
+ * @id: an id, used to cancel later (0 if not needed)
+ * @file_name: the file name to play
+ * @description: the localized description of the event that triggered this alert
+ * @for_event: (allow-none): a #ClutterEvent in response to which the sound is played
+ *
+ * Like shell_global_play_theme_sound(), but with an explicit path
+ * instead of a themed sound.
+ */
+void
+shell_global_play_sound_file (ShellGlobal  *global,
+                              guint         id,
+                              const char   *file_name,
+                              const char   *description,
+                              ClutterEvent *for_event)
+{
+  ca_proplist *props;
+
+  ca_proplist_create (&props);
+  build_ca_proplist_for_event (props, CA_PROP_MEDIA_FILENAME, file_name, description, for_event);
+
+  ca_context_play_full (global->sound_context, id, props, NULL, NULL);
+
+  ca_proplist_destroy (props);
+}
+
 /**
  * shell_global_cancel_theme_sound:
  * @global: the #ShellGlobal
index 88b40bed58ddb0cf01c7982f2b4e42a61f04af15..a8df0f5bc82f46444b40cc015e8659a1bab25990 100644 (file)
@@ -124,6 +124,19 @@ void     shell_global_play_theme_sound_full     (ShellGlobal  *global,
                                                  ClutterEvent *for_event,
                                                  const char   *application_id,
                                                  const char   *application_name);
+void     shell_global_play_sound_file           (ShellGlobal  *global,
+                                                 guint         id,
+                                                 const char   *file_name,
+                                                 const char   *description,
+                                                 ClutterEvent *for_event);
+void     shell_global_play_sound_file_full      (ShellGlobal  *global,
+                                                 guint         id,
+                                                 const char   *file_name,
+                                                 const char   *description,
+                                                 ClutterEvent *for_event,
+                                                 const char   *application_id,
+                                                 const char   *application_name);
+
 void     shell_global_cancel_theme_sound        (ShellGlobal  *global,
                                                  guint         id);